package services

import (
	"encoding/csv"
	"encoding/json"
	"filfoxexporter/models"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"time"
)

const (
	Messages  string = "messages"
	Transfers string = "transfers"
)

var sleeps int64 = 5
var MultipleMap []float64

func init() {
	MultipleMap = append(MultipleMap, 1.2)
	MultipleMap = append(MultipleMap, 1.1)
	MultipleMap = append(MultipleMap, 1.05)
	MultipleMap = append(MultipleMap, 1.01)
	MultipleMap = append(MultipleMap, 1.005)
	MultipleMap = append(MultipleMap, 1.001)
}

func getMiddlePage(which, to, addr string, typo string) (middle int64, where int) {
	times := 1
	where = -1
	page := int64(0)

	for {
		url := fmt.Sprintf("https://filfox.info/api/v1/address/%v/%v?pageSize=20&page=%v", addr, which, page)

		if typo != "" {
			url = fmt.Sprintf("%v&method=%v", url, typo)
		}

		resp, gerr := http.Get(url)
		if gerr != nil {
			fmt.Printf("error for http get: %v", gerr)
			time.Sleep(time.Second * 10)
			page--
			continue
		}

		bytes, rerr := ioutil.ReadAll(resp.Body)
		if rerr != nil {
			fmt.Printf("error for read body: %v", rerr)
			return
		}

		fmt.Printf("get middle read page %v \n", page)

		if which == Messages {
			wrapper := models.MessageWrapper{}
			jerr := json.Unmarshal(bytes, &wrapper)
			if jerr != nil {
				fmt.Printf("error for json unmarshal body: %v", jerr)
				return
			}

			if len(wrapper.Messages) > 0 {
				msg := wrapper.Messages[0]
				time := time.Unix(msg.Timestamp, 64)
				t := time.Format("2006-01-02 15:04:05")
				if t < to {
					where = 1
				} else {
					where = -1
				}
			}

			if wrapper.TotalCount > 0 {
				middle = wrapper.TotalCount / 20 / 2
				page = middle
			}
		} else if which == Transfers {
			wrapper := models.TransWrapper{}
			jerr := json.Unmarshal(bytes, &wrapper)
			if jerr != nil {
				fmt.Printf("error for json unmarshal body: %v", jerr)
				return
			}

			if len(wrapper.Transfers) > 0 {
				msg := wrapper.Transfers[0]
				time := time.Unix(msg.Timestamp, 64)
				t := time.Format("2006-01-02 15:04:05")
				if t < to {
					where = 1
				} else {
					where = -1
				}
			}

			if wrapper.TotalCount > 0 {
				middle = wrapper.TotalCount / 20 / 2
				page = middle
			}
		}

		if times == 2 {
			break
		}

		times = times + 1

		time.Sleep(time.Second * time.Duration(sleeps))
	}

	return middle, where
}

func getPageData(page int64, which, to, typo, addr string) (lesser int) {
	for {
		url := fmt.Sprintf("https://filfox.info/api/v1/address/%v/%v?pageSize=20&page=%v", addr, which, page)

		if typo != "" {
			url = fmt.Sprintf("%v&method=%v", url, typo)
		}

		resp, gerr := http.Get(url)
		if gerr != nil {
			fmt.Printf("error for http get: %v", gerr)
			time.Sleep(time.Second * 10)
			page--
			continue
		}

		bytes, rerr := ioutil.ReadAll(resp.Body)
		if rerr != nil {
			fmt.Printf("error for read body: %v", rerr)
			return
		}

		fmt.Printf("%v calculate start page read page %v \n", addr, page)

		if which == Messages {
			wrapper := models.MessageWrapper{}
			jerr := json.Unmarshal(bytes, &wrapper)
			if jerr != nil {
				fmt.Printf("error for json unmarshal body: %v", jerr)
				return
			}

			if wrapper.TotalCount == 0 {
				lesser = 1
				break
			}

			if len(wrapper.Messages) > 0 {
				msg := wrapper.Messages[0]
				time := time.Unix(msg.Timestamp, 64)
				t := time.Format("2006-01-02 15:04:05")
				if msg != nil {
					if t < to {
						lesser = 1
					} else {
						lesser = -1
					}

					break
				}
			}
		} else if which == Transfers {
			wrapper := models.TransWrapper{}
			jerr := json.Unmarshal(bytes, &wrapper)
			if jerr != nil {
				fmt.Printf("error for json unmarshal body: %v", jerr)
				return
			}

			if wrapper.TotalCount == 0 {
				lesser = 1
				break
			}

			if len(wrapper.Transfers) > 0 {
				msg := wrapper.Transfers[0]
				time := time.Unix(msg.Timestamp, 64)
				t := time.Format("2006-01-02 15:04:05")
				if msg != nil {
					if t < to {
						lesser = 1
					} else {
						lesser = -1
					}

					break
				}
			}
		}

		time.Sleep(time.Second * time.Duration(sleeps))
	}

	return
}

func Message(from, to string, address []string, typo string, dur int64) (success bool, err error) {
	sleeps = dur

	for _, add := range address {
		tempFilename := "./data/a.csv"
		csvFilename := fmt.Sprintf("./data/msg-%v.csv", add)

		f, cerr := os.Create(tempFilename)
		if cerr != nil {
			fmt.Printf("error for create csv file: %v", cerr)
			return
		}
		f.WriteString("\xEF\xBB\xBF")
		w := csv.NewWriter(f)

		page := int64(0)
		middle, where := getMiddlePage(Messages, to, add, typo)
		if where > 0 {
			page = Less(middle, Messages, to, typo, add)
		} else {
			page = Greater(middle, Messages, to, typo, add)
		}

		fmt.Printf("%v messages get start page:%v \n", add, page)

		for {
			url := fmt.Sprintf("https://filfox.info/api/v1/address/%v/messages?pageSize=20&page=%v", add, page)

			if typo != "" {
				url = fmt.Sprintf("%v&method=%v", url, typo)
			}

			resp, gerr := http.Get(url)
			if gerr != nil {
				fmt.Printf("error for http get: %v", gerr)
				time.Sleep(time.Second * 10)
				page--
				continue
			}

			bytes, rerr := ioutil.ReadAll(resp.Body)
			if rerr != nil {
				fmt.Printf("error for read body: %v", rerr)
				return
			}

			wrapper := models.MessageWrapper{}
			jerr := json.Unmarshal(bytes, &wrapper)
			if jerr != nil {
				fmt.Printf("error for json unmarshal body: %v", jerr)
				return
			}

			if len(wrapper.Messages) == 0 {
				break
			}

			fmt.Printf("%v read page %v, time:%v \n", add, page, time.Now())

			complete := false
			for _, msg := range wrapper.Messages {
				time := time.Unix(msg.Timestamp, 64)
				t := time.Format("2006-01-02 15:04:05")

				if t < from {
					complete = true
					break
				}

				if t <= to && t >= from {
					exited := false
					if msg.Receipt != nil {
						exited = msg.Receipt.ExitCode == 0
					}
					e := strconv.FormatBool(exited)
					height := strconv.FormatInt(msg.Height, 10)
					arr := []string{msg.Cid, height, t, msg.From, msg.To, msg.Method, msg.Value, e}

					werr := w.Write(arr)
					if werr != nil {
						fmt.Printf("error for write csv file: %v", werr)
						return
					}

					w.Flush()
				}
			}

			if complete {
				fmt.Printf("%v complete %v, time:%v \n", add, page, time.Now())

				break
			}

			time.Sleep(time.Second * time.Duration(sleeps))
			page++
		}

		f.Close()
		os.Rename("./data/a.csv", csvFilename)
	}

	return true, nil
}

func Trans(from, to string, address []string, typo string, dur int64) (success bool, err error) {
	sleeps = dur

	for _, add := range address {
		tempFilename := "./data/b.csv"
		csvFilename := fmt.Sprintf("./data/trans-%v.csv", add)

		f, cerr := os.Create(tempFilename)
		if cerr != nil {
			fmt.Printf("error for create csv file: %v", cerr)
			return
		}

		f.WriteString("\xEF\xBB\xBF")
		w := csv.NewWriter(f)

		page := int64(0)
		middle, where := getMiddlePage(Transfers, to, add, typo)
		//if t < to {
		//	where = 1
		//} else {
		//	where = -1
		//}
		if where > 0 {
			page = Less(middle, Transfers, to, typo, add)
		} else {
			page = Greater(middle, Transfers, to, typo, add)
		}

		fmt.Printf("%v transfers get start page:%v \n", add, page)

		for {
			url := fmt.Sprintf("https://filfox.info/api/v1/address/%v/transfers?pageSize=20&page=%v", add, page)

			if typo != "" {
				url = fmt.Sprintf("%v&type=%v", url, typo)
			}

			resp, gerr := http.Get(url)
			if gerr != nil {
				fmt.Printf("error for http get: %v", gerr)
				time.Sleep(time.Second * 10)
				page--
				continue
			}

			bytes, rerr := ioutil.ReadAll(resp.Body)
			if rerr != nil {
				fmt.Printf("error for read body: %v", rerr)
				return
			}

			wrapper := models.TransWrapper{}
			jerr := json.Unmarshal(bytes, &wrapper)
			if jerr != nil {
				fmt.Printf("error for json unmarshal body: %v", jerr)
				return
			}

			if len(wrapper.Transfers) == 0 {
				break
			}

			fmt.Printf("%v read page %v, time:%v \n", add, page, time.Now())

			complete := false
			for _, msg := range wrapper.Transfers {
				time := time.Unix(msg.Timestamp, 64)
				t := time.Format("2006-01-02 15:04:05")

				if t < from {
					complete = true
					break
				}

				if t <= to && t >= from {
					height := strconv.FormatInt(msg.Height, 10)
					arr := []string{msg.Message, height, t, msg.From, msg.To, msg.Type, msg.Value}

					werr := w.Write(arr)
					if werr != nil {
						fmt.Printf("error for write csv file: %v", werr)
						return
					}

					w.Flush()
				}
			}

			if complete {
				fmt.Printf("%v complete %v, time:%v \n", add, page, time.Now())

				break
			}

			time.Sleep(time.Second * time.Duration(sleeps))
			page++
		}

		f.Close()
		os.Rename("./data/b.csv", csvFilename)
	}

	return true, nil
}

func FileExist(path string) bool {
	_, err := os.Lstat(path)
	return !os.IsNotExist(err)
}

func Greater(page int64, which, to, typo, addr string) (aim int64) {
	prepage := page
	for _, ti := range MultipleMap {
		for {
			prepage = page
			page = int64(float64(page) * ti)

			lesser := getPageData(page, which, to, typo, addr)

			//if t < to {
			//	lesser = 1
			//} else {
			//	lesser = -1
			//}
			if lesser > 0 {
				page = prepage
				break
			}

			fmt.Printf("greater cal  prepage:%v, page:%v \n", prepage, page)
			if prepage == page {
				break
			}
		}
	}

	return page
}

func Less(page int64, which, to, typo, addr string) (aim int64) {
	times := 1
	prepage := page
	for _, ti := range MultipleMap {
		for {
			prepage = page
			page = int64(float64(page) / ti)

			lesser := getPageData(page, which, to, typo, addr)

			if lesser < 0 {
				if times != len(MultipleMap) {
					page = prepage
				}

				break
			}
		}

		times = times + 1
	}

	return page
}
